#include "stdafx.h"
#include "csobject.h"

CS_NAMESPACE_BEGIN

csDestroyNotify::csDestroyNotify(csObject* pSender)
    : csObjectNotify(csObject::Destroy, pSender)
{
}

class csObjectData
{
public:
    ~csObjectData();
    csString objectName() const;
    void setObjectName(const csString& objectName);
    csObject* parent() const;
    void setParent(csObject* pObject);

    bool hasProperty(const char* name) const;
    csVariant property(const char* name) const;
    void setProperty(const char* name, const csVariant& value);
    csVariantMap propertys() const;
    bool hasMemberProperty(const char* name) const;
    csVariant memberProperty(const char* name) const;
    void setMemberProperty(const char* name, const csVariant& value);
    csVariantMap memberPropertys() const;
    void insertMemberProperty(const csString& name, csGenericProperty* pProperty);
    bool hasValueProperty(const char* name) const;
    csVariant valueProperty(const char* name) const;
    void setValueProperty(const char* name, const csVariant& value);
    csVariantMap valuePropertys() const;
    void removeValueProperty(const char* name);
    void clearValuePropertys();
private:
    csString m_objectName;
    csObject* m_pParent = nullptr;
    csMap<csString, csGenericProperty*> m_memberPropertyMap;
    csMap<csString, csVariant> m_valuePropertyValueMap;
    csMap<csString, csGenericMethod*> m_methodMap;
    std::list<csObject*> filters;
    std::list<csObject*> recivers;
    friend csObject;
};

csObjectData::~csObjectData()
{
    //delete property
    for (auto pair : m_memberPropertyMap)
    {
        delete pair.second;
    }
    //delete method
    for (auto pair : m_methodMap)
    {
        delete pair.second;
    }
}

csString csObjectData::objectName() const
{
    return m_objectName;
}

void csObjectData::setObjectName(const csString& objectName)
{
    m_objectName = objectName;
}

csObject* csObjectData::parent() const
{
    return m_pParent;
}

void csObjectData::setParent(csObject* pObject)
{
    m_pParent = pObject;
}

bool csObjectData::hasProperty(const char* name) const
{
    return hasMemberProperty(name) || hasValueProperty(name);
}

csVariant csObjectData::property(const char* name) const
{
    return hasMemberProperty(name) ? memberProperty(name) : valueProperty(name);
}

void csObjectData::setProperty(const char* name, const csVariant& value)
{
    hasMemberProperty(name) ? setMemberProperty(name, value) : setValueProperty(name, value);
}

csVariantMap csObjectData::propertys() const
{
    csVariantMap members = memberPropertys();
    csVariantMap values = valuePropertys();
    members.insert(values.begin(), values.end());
    return members;
}

bool csObjectData::hasMemberProperty(const char* name) const
{
    return m_memberPropertyMap.find(name) != m_memberPropertyMap.end();
}

csVariant csObjectData::memberProperty(const char* name) const
{
    auto it = m_memberPropertyMap.find(name);
    return it != m_memberPropertyMap.end() && it->second != nullptr ? it->second->value() : csVariant();
}

void csObjectData::setMemberProperty(const char* name, const csVariant& value)
{
    auto it = m_memberPropertyMap.find(name);
    if (it != m_memberPropertyMap.end() && it->second != nullptr)
        it->second->setValue(value);
}

csVariantMap csObjectData::memberPropertys() const
{
    csVariantMap members;
    for (auto pair : m_memberPropertyMap)
    {
        if (pair.second)
            members[pair.first] = pair.second->value();
    }
    return members;
}

void csObjectData::insertMemberProperty(const csString& name, csGenericProperty* pProperty)
{
    m_memberPropertyMap[name] = pProperty;
}

bool csObjectData::hasValueProperty(const char* name) const
{
    return m_valuePropertyValueMap.find(name) != m_valuePropertyValueMap.end();
}

csVariant csObjectData::valueProperty(const char* name) const
{
    auto it = m_valuePropertyValueMap.find(name);
    return it != m_valuePropertyValueMap.end() ? it->second : csVariant();
}

void csObjectData::setValueProperty(const char* name, const csVariant& value)
{
    auto it = m_valuePropertyValueMap.find(name);
    if (it != m_valuePropertyValueMap.end())
        it->second = value;
}

csVariantMap csObjectData::valuePropertys() const
{
    return m_valuePropertyValueMap;
}

void csObjectData::removeValueProperty(const char* name)
{
    auto it = m_valuePropertyValueMap.find(name);
    if (it != m_valuePropertyValueMap.end())
        m_valuePropertyValueMap.erase(it);
}

void csObjectData::clearValuePropertys()
{
    m_valuePropertyValueMap.clear();
}

csObject::csObject(csObject* pParent)
    : m_d(new csObjectData())
{
    setParent(pParent);
}

csObject::~csObject()
{
    delete m_d;
}

csString csObject::objectName() const
{
    return m_d->objectName();
}

void csObject::setObjectName(const csString& objectName)
{
    m_d->setObjectName(objectName);
}

csObject* csObject::parent() const
{
    return m_d->parent();
}

void csObject::setParent(csObject* pObject)
{
    m_d->setParent(pObject);
}

bool csObject::hasProperty(const char* name) const
{
    return m_d->hasProperty(name);
}

csVariant csObject::property(const char* name) const
{
    return m_d->property(name);
}

void csObject::setProperty(const char* name, const csVariant& value)
{
    m_d->setProperty(name, value);
}

csVariantMap csObject::propertys() const
{
    return m_d->propertys();
}

bool csObject::hasMemberProperty(const char* name) const
{
    return m_d->hasMemberProperty(name);
}

csVariant csObject::memberProperty(const char* name) const
{
    return m_d->memberProperty(name);
}

void csObject::setMemberProperty(const char* name, const csVariant& value)
{
    m_d->setMemberProperty(name, value);
}

csVariantMap csObject::memberPropertys() const
{
    return m_d->memberPropertys();
}

bool csObject::hasValueProperty(const char* name) const
{
    return m_d->hasValueProperty(name);
}

csVariant csObject::valueProperty(const char* name) const
{
    return m_d->valueProperty(name);
}

void csObject::setValueProperty(const char* name, const csVariant& value)
{
    m_d->setValueProperty(name, value);
}

csVariantMap csObject::valuePropertys() const
{
    return m_d->valuePropertys();
}

void csObject::removeValueProperty(const char* name)
{
    m_d->removeValueProperty(name);
}

void csObject::clearValuePropertys()
{
    m_d->clearValuePropertys();
}

bool csObject::hasMethod(const char* signature) const
{
    return m_d->m_methodMap.find(signature) != m_d->m_methodMap.end();
}

bool csObject::invoke(
    const char* method,
    csGenericReturnArgument ret,
    csGenericArgument arg1,
    csGenericArgument arg2,
    csGenericArgument arg3,
    csGenericArgument arg4,
    csGenericArgument arg5)
{
    std::list<csGenericArgument> args = { arg1, arg2, arg3, arg4, arg5 };
    std::string signature = generateSignature(method, args);
    std::map<std::string, csGenericMethod*>::iterator it = m_d->m_methodMap.find(signature);
    if (it == m_d->m_methodMap.end())
    {
        std::cout << className() << "::invoke: No such method " << signature << std::endl;
        return false;
    }
    csGenericMethod* pMethod = it->second;
    if (!pMethod)
    {
        std::cout << className() << "::invoke: Method is null " << signature << std::endl;
        return false;
    }
    return pMethod->invoke(ret, arg1, arg2, arg3, arg4, arg5);
}

bool csObject::invoke(
    const char* method,
    csGenericArgument arg1,
    csGenericArgument arg2,
    csGenericArgument arg3,
    csGenericArgument arg4,
    csGenericArgument arg5)
{
    return invoke(method, csGenericReturnArgument(), arg1, arg2, arg3, arg4, arg5);
}

void csObject::attach(csObject* pObject)
{
    m_d->recivers.push_back(pObject);
}

void csObject::detach(csObject* pObject)
{
    m_d->recivers.remove(pObject);
}

void csObject::installFilter(csObject* pObject)
{
    m_d->filters.push_back(pObject);
}

void csObject::removeFilter(csObject* pObject)
{
    m_d->filters.remove(pObject);
}

void csObject::registerProperty(const csString& name, csGenericProperty* pProperty)
{
    m_d->insertMemberProperty(name, pProperty);
}

bool csObject::registerMethod(const std::string& name, csGenericMethod* method)
{
    m_d->m_methodMap[name] = method;
    return true;
}

std::string csObject::generateSignature(const char* method, const std::list<csGenericArgument>& args)
{
    std::string signature = method;
    bool hasArg = false;
    signature += "(";
    for (const csGenericArgument& arg : args)
    {
        if (arg.data() == nullptr)
        {
            break;
        }
        else
        {
            signature += std::string(hasArg ? "," : "") + arg.name();
            hasArg = true;
        }
    }
    signature += ")";
    return signature;
}

void csObject::send(csNotify* pNotify)
{
    if (dishFilter(pNotify))
        return;
    dishReciver(pNotify);
}

bool csObject::dishFilter(csNotify* pNotify)
{
    for (auto object : m_d->filters)
    {
        if (object->filter(pNotify))
            return true;
    }
    return false;
}

bool csObject::filter(csNotify* pNotify)
{
    CS_UNUSED(pNotify);
    return false;
}

void csObject::dishReciver(csNotify* pNotify)
{
    for (auto object : m_d->recivers)
    {
        object->recive(pNotify);
    }
}

void csObject::recive(csNotify* pNotify)
{
    if (pNotify->type() == csObject::Destroy)
        reciveDestroy(static_cast<csDestroyNotify*>(pNotify));
    CS_UNUSED(pNotify);
}

void csObject::reciveDestroy(csDestroyNotify* pNotify)
{
}

CS_NAMESPACE_END
